# tools/popup_handler.py
"""
统一处理 UI 测试中出现的即时弹窗（如提示、警告、确认框等）
支持自动关闭、日志记录、截图保存、自定义响应策略
"""
import time
from datetime import datetime
from typing import Tuple, Optional

from pywinauto import Desktop

from config.settings import SCREENSHOTS_DIR, POPUP_SCREENSHOT_PREFIX

from tools.logger import log_info, log_warning, log_error


class PopupHandler:
    """弹窗处理器：统一处理常见弹窗"""
    # 配置：截图保存路径
    SCREENSHOT_DIR = SCREENSHOTS_DIR
    SCREENSHOT_PREFIX = POPUP_SCREENSHOT_PREFIX

    # 弹窗标题匹配规则（支持多个可能的标题）
    DIALOG_TITLES = [
        "条码关联系统-提示",
        "提示",
        "警告",
        "错误",
        "确认"
    ]

    # 按钮 AutomationId 或 Title 的查找优先级
    BUTTON_STRATEGIES = [
        {"auto_id": "okbtn"},
        {"title": "确定"},
        {"title": "是"},
        {"title": "好"},
    ]

    @classmethod
    def _find_dialog(cls, timeout: float = 0) -> Optional:
        """在桌面层级查找任意匹配的弹窗"""
        desktop = Desktop(backend='win32')
        for title in cls.DIALOG_TITLES:
            try:
                dialog = desktop.window(title=title)
                if dialog.exists(timeout=timeout):
                    return dialog
            except Exception:
                continue
        return None

    @classmethod
    def _get_dialog_text(cls, dialog) -> list:
        """提取弹窗内所有文本内容"""
        try:
            return [
                e.window_text().strip()
                for e in dialog.descendants(auto_id='Tiplbl')
                if e.window_text().strip()
            ]
        except Exception as e:
            log_warning(f"⚠️ 提取弹窗文本失败: {e}")
            return []

    @classmethod
    def _capture_screenshot(cls, dialog, reason="unknown"):
        """对弹窗进行截图保存"""
        try:
            time.sleep(0.1)
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
            filename = f"{cls.SCREENSHOT_PREFIX}_{reason}_{timestamp}.png"
            filepath = cls.SCREENSHOT_DIR / filename

            # 使用 capture_as_image() 截取弹窗图像
            img = dialog.capture_as_image()
            img.save(filepath)
            log_info(f"📸 弹窗已截图保存: {filepath}")
            return filepath
        except Exception as e:
            log_error(f"❌ 截图失败: {e}")
            return None

    @classmethod
    def _click_button(cls, dialog):
        """尝试多种策略点击确认类按钮"""
        for strategy in cls.BUTTON_STRATEGIES:
            try:
                btn = dialog.child_window(**strategy)
                if btn.exists(timeout=2):
                    btn.click_input()  # 使用 UIA API 点击，更稳定
                    log_info(f"✅ 已点击按钮: {strategy}")
                    return True
            except Exception as e:
                log_warning(f"⚠️ 按策略 {strategy} 点击按钮失败: {e}")
                continue

        # 最终手段：发送 ENTER
        try:
            dialog.set_focus()
            dialog.type_keys('{ENTER}')
            log_info("✅ 通过发送 {ENTER} 关闭弹窗")
            return True
        except Exception as e:
            log_error(f"❌ 发送 ENTER 也失败: {e}")
            return False

    @classmethod
    def handle(cls, timeout: float = 3.0, raise_on_error: bool = False) -> Tuple[bool, str, Optional[str]]:
        """
        处理可能存在的弹窗

        :param timeout: 等待弹窗出现的超时时间
        :param raise_on_error: 是否在出现错误类弹窗时抛出异常
        :return: (是否出现弹窗, 弹窗文本内容，截图文件路径 或 None)
        """
        start_time = time.time()

        # 等待弹窗出现
        dialog = None
        while time.time() - start_time < timeout:
            dialog = cls._find_dialog()
            if dialog is not None:
                break
            time.sleep(0.2)

        if not dialog:
            return False, "", None

        log_warning("⚠️ 检测到弹窗出现！开始处理...")

        # 提取文本
        texts = cls._get_dialog_text(dialog)
        full_text = "\n".join(texts)
        log_warning(f"📋 弹窗内容:\n{full_text}")

        # 截图（所有情况都截图，便于回溯）
        screenshot_path = cls._capture_screenshot(dialog, reason="general")

        # 判断是否为“错误”类弹窗（可用于决定是否中断）
        is_error = any(
            kw in full_text
            for kw in ["错误", "失败", "异常", "不允许", "无效", "未找到", "格式不正确", "已绑定"]
        )

        if is_error:
            log_error("🔴 识别为【错误类弹窗】")

        # 尝试关闭弹窗
        closed = cls._click_button(dialog)
        if not closed:
            log_error("❌ 无法关闭弹窗，请检查 UI 状态！")

        # 根据配置决定是否抛出异常
        if raise_on_error and is_error:
            raise RuntimeError(f"出现错误弹窗，中断流程：{full_text}")

        return True, full_text, screenshot_path

    @classmethod
    def wait_and_handle(cls, timeout: float = 5.0, interval: float = 0.5):
        """
        持续监听弹窗（用于异步操作后）
        """
        start = time.time()
        while time.time() - start < timeout:
            popup, text, screenshot_path = cls.handle(timeout=0)
            if popup:
                return popup, text, screenshot_path
            time.sleep(interval)
        return False, "", None